package tech.aiflowy.common.web.controller;

import tech.aiflowy.common.ai.ChatManager;
import tech.aiflowy.common.ai.util.AiSqlUtil;
import tech.aiflowy.common.domain.Result;
import tech.aiflowy.common.entity.LoginAccount;
import tech.aiflowy.common.tree.Tree;
import tech.aiflowy.common.util.Maps;
import tech.aiflowy.common.util.SqlOperatorsUtil;
import tech.aiflowy.common.util.SqlUtil;
import tech.aiflowy.common.web.exceptions.ProgramException;
import tech.aiflowy.common.web.jsonbody.JsonBody;
import tech.aiflowy.common.satoken.util.SaTokenUtil;
import com.agentsflex.core.prompt.TextPrompt;
import com.agentsflex.core.prompt.template.TextPromptTemplate;
import com.alibaba.fastjson.JSON;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.query.SqlOperators;
import com.mybatisflex.core.service.IService;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import com.mybatisflex.core.util.StringUtil;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.*;

public class BaseCurdController<S extends IService<M>, M> extends BaseController {


    protected final S service;

    public BaseCurdController(S service) {
        this.service = service;
    }

    /**
     * 添加（保存）数据
     *
     * @param entity 表生成内容配置
     * @return {@code Result.errorCode == 0} 添加成功，否则添加失败
     */
    @PostMapping("save")
    public Result save(@JsonBody M entity) {
        Result result = onSaveOrUpdateBefore(entity, true);
        if (result != null) return result;

        if (entity == null) {
            throw new NullPointerException("entity is null");
        }
        LoginAccount loginAccount = SaTokenUtil.getLoginAccount();
        commonFiled(entity,loginAccount.getId(),loginAccount.getTenantId(),loginAccount.getDeptId());
        boolean success = service.save(entity);
        onSaveOrUpdateAfter(entity, true);
        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entity.getClass());
        Object[] pkArgs = tableInfo.buildPkSqlArgs(entity);
        return Result.create(success).set("id", pkArgs);
    }

    /**
     * 根据主键删除数据，id 需通过 json 传入，例如：
     * <pre>
     * {
     *   "id":123
     * }
     * <pre/>
     *
     *
     * @param id 主键
     * @return {@code Result.errorCode == 0} 删除成功，否则删除失败
     */
    @PostMapping("remove")
    @Transactional
    public Result remove(@JsonBody(value = "id", required = true) Serializable id) {
        List<Serializable> ids = Collections.singletonList(id);
        Result result = onRemoveBefore(ids);
        if (result != null) return result;
        boolean success = service.removeById(id);
        onRemoveAfter(ids);
        return Result.create(success);
    }


    /**
     * 根据多个主键删数据内容，id 需通过 json 传入，例如：
     * <pre>
     * {
     *   "ids":[123, 234, 222]
     * }
     * <pre/>
     *
     * @param ids 主键
     * @return {@code Result.errorCode == 0} 删除成功，否则删除失败
     */
    @PostMapping("removeBatch")
    @Transactional
    public Result removeBatch(@JsonBody(value = "ids", required = true) Collection<Serializable> ids) {
        if (ids == null || ids.isEmpty()) {
            return Result.fail();
        }
        Result result = onRemoveBefore(ids);
        if (result != null) return result;
        boolean success = service.removeByIds(ids);
        onRemoveAfter(ids);
        return Result.create(success);
    }

    /**
     * 根据主键更新内容
     *
     * @param entity 实体类数据
     * @return {@code Result.errorCode == 0} 更新成功，否则更新失败
     */
    @PostMapping("update")
    public Result update(@JsonBody M entity) {
        Result result = onSaveOrUpdateBefore(entity, false);
        if (result != null) return result;
        boolean success = service.updateById(entity);
        onSaveOrUpdateAfter(entity, false);
        return Result.create(success);
    }

    /**
     * 查询所有所有数据
     *
     * @return 所有数据
     */
    @GetMapping("list")
    public Result list(M entity, Boolean asTree, String sortKey, String sortType) {
        QueryWrapper queryWrapper = QueryWrapper.create(entity, buildOperators(entity));
        queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
        List<M> list = Tree.tryToTree(service.list(queryWrapper), asTree);
        return Result.success(list);
    }


    /**
     * 根据表主键查询数据详情。
     *
     * @param id 主键值
     * @return 内容详情
     */
    @GetMapping("detail")
    public Result detail(String id) {
        if (tech.aiflowy.common.util.StringUtil.noText(id)) {
            return Result.fail(0, "id is null");
        }
        return Result.success(service.getById(id));
    }


    /**
     * 分页查询数据列表
     *
     * @param request    查询数据
     * @param sortKey    排序字段
     * @param sortType   排序方式 asc | desc
     * @param pageNumber 当前页码
     * @param pageSize   每页的数据量
     * @return 查询的结果集
     */
    @GetMapping("page")
    public Result page(HttpServletRequest request, String sortKey, String sortType, Long pageNumber, Long pageSize) {
        if (pageNumber == null || pageNumber < 1) {
            pageNumber = 1L;
        }
        if (pageSize == null || pageSize < 1) {
            pageSize = 10L;
        }

        QueryWrapper queryWrapper = buildQueryWrapper(request);
        queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
        return Result.success(queryPage(new Page<>(pageNumber, pageSize), queryWrapper));
    }


    protected QueryWrapper buildQueryWrapper(HttpServletRequest request) {

        QueryWrapper queryWrapper = new QueryWrapper();

        Map<String, String[]> parameterMap = request.getParameterMap();
        if (parameterMap == null || parameterMap.isEmpty()) {
            return queryWrapper;
        }

        Map<String, String> propertyColumnMapping = TableInfoFactory.ofEntityClass(getEntityClass())
                .getPropertyColumnMapping();
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String paramKey = entry.getKey();
            if (StringUtil.hasText(paramKey) && !paramKey.endsWith(OperatorBuilder.operatorSuffix) && propertyColumnMapping.containsKey(paramKey)) {
                String columnName = propertyColumnMapping.get(paramKey);
                String[] values = entry.getValue();
                if (values != null && values.length > 0 && StringUtil.hasText(values[0])) {
                    String op = request.getParameter(paramKey + OperatorBuilder.operatorSuffix);
                    if (StringUtil.hasText(op)) {
                        OperatorBuilder.buildOperator(queryWrapper, columnName, op.trim(), values);
                    } else {
                        if (values.length == 2) {
                            queryWrapper.between(columnName, values[0], values[1]);
                        } else {
                            String value = values[0];
                            if (StringUtil.isNumeric(value)) {
                                queryWrapper.eq(columnName, value);
                            } else {
                                queryWrapper.like(columnName, value);
                            }
                        }
                    }
                }
            }
        }

        return queryWrapper;
    }


    @PostMapping("intelligentFilling")
    public Result intelligentFilling(@JsonBody("content") String content) {
        Class<?> entityClass = getEntityClass();
        if (entityClass == null) {
            return Result.success();
        }

        String templateString = "请根据如下的 DDL 语句，帮我分析出用户给出的内容，可能对应的是哪个字段，并使用 JSON 数据格式的方式返回，" +
                "注意，只需要返回 JSON 的数据内容，不需要对此进行解释以，不需要及除了 JSON 数据以外的其他任何内容。\n" +
                "\n" +
                "假设 DDL 内容为：\n" +
                "\n" +
                "```sql\n" +
                "CREATE TABLE `tb_sys_account` (\n" +
                "  `id` bigint(20) unsigned NOT NULL COMMENT '主键ID',\n" +
                "  `login_name` varchar(64) DEFAULT NULL COMMENT '登录账号',\n" +
                "  `nickname` varchar(128) DEFAULT NULL COMMENT '昵称',\n" +
                "  `mobile` varchar(32) DEFAULT NULL COMMENT '手机电话',\n" +
                "  `email` varchar(64) DEFAULT NULL COMMENT '邮件'\n" +
                ") ENGINE=InnoDB COMMENT='账号信息表';\n" +
                "```\n" +
                "\n" +
                "假设用户给出的内容：\n" +
                "\n" +
                "张三  18611223344\n" +
                "\n" +
                "你应该返回如下的 JSON 内容：\n" +
                "\n" +
                "```json\n" +
                "{\n" +
                "  \"nickname\": \"张三\",\n" +
                "  \"mobile\": \"18611223344\"\n" +
                "}\n" +
                "```\n" +
                "\n" +
                "现在，真实的 DDL 内容如下：\n" +
                "\n" +
                "```sql\n" +
                "{similarilyDDL}" +
                "```\n" +
                "用户给出的内容如下：\n" +
                "{content}";

        TextPromptTemplate template = new TextPromptTemplate(templateString);
        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass);
        TextPrompt prompt = template.format(Maps.of("similarilyDDL", AiSqlUtil.getSimilarilyDDL(tableInfo))
                .set("content", content));
        String maybeJson = ChatManager.getInstance().chat(prompt);
        if (maybeJson == null || !maybeJson.contains("```")) {
            return Result.success();
        } else {
            maybeJson = maybeJson.replace("```json", "").replace("```", "");
        }

        return Result.success("result", JSON.parse(maybeJson));
    }


    protected Class<?> getEntityClass() {
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            return (Class<M>) ((ParameterizedType) type).getActualTypeArguments()[1];
        }

        return null;
    }


    /**
     * 方便子类复写，构建自己的 SqlOperators
     *
     * @param entity 实体类
     * @return SqlOperators
     */
    protected SqlOperators buildOperators(M entity) {
        return entity == null ? SqlOperators.empty() : SqlOperatorsUtil.build(entity.getClass());
    }

    protected String getDefaultOrderBy() {
        return "id desc";
    }

    /**
     * 方便子类复写，构建自己的 orderBy
     *
     * @param sortKey        排序字段
     * @param sortType       排序类型
     * @param defaultOrderBy 默认方式内容
     * @return orderBy 的内容，返回 null 或者 空字符串，表示不参与排序
     */
    protected String buildOrderBy(String sortKey, String sortType, String defaultOrderBy) {
        sortKey = StringUtil.camelToUnderline(sortKey);
        return SqlUtil.buildOrderBy(sortKey, sortType, defaultOrderBy);
    }

    protected Page<M> queryPage(Page<M> page, QueryWrapper queryWrapper) {
        return service.page(page, queryWrapper);
    }

    protected Result onSaveOrUpdateBefore(M entity, boolean isSave) {
        return null;
    }

    protected void onSaveOrUpdateAfter(M entity, boolean isSave) {
        //void
    }

    protected Result onRemoveBefore(Collection<Serializable> ids) {
        return null;
    }

    protected void onRemoveAfter(Collection<Serializable> ids) {
        //void
    }

    protected void commonFiled(Object t, BigInteger userId, BigInteger tenantId, BigInteger deptId) {
        Method[] methods = t.getClass().getMethods();
        try {
            for (Method m : methods) {
                String name = m.getName();
                if ("setDeptId".equals(name)) {
                    m.invoke(t, deptId);
                }
                if ("setTenantId".equals(name)) {
                    m.invoke(t, tenantId);
                }
                if ("setCreatedBy".equals(name)) {
                    m.invoke(t, userId);
                }
                if ("setModifiedBy".equals(name)) {
                    m.invoke(t, userId);
                }
                if ("setCreated".equals(name)) {
                    m.invoke(t, new Date());
                }
                if ("setModified".equals(name)) {
                    m.invoke(t, new Date());
                }
            }
        } catch (Exception e) {
            throw new ProgramException("commonFiled反射出错：" + e.getMessage());
        }
    }
}
